<?php
/**
 * Mechanism
 * 
 * LICENSE
 * 
 * This file is private intellectual property of the author. 
 * All rights reserved. Copying and illegal using is punishable by law.
 * 
 * @copyright Copyright (c) 2011 BreatheInMyVoid ( BreatheInMyVoid@gmail.com )
 */

namespace Mechanism\Router\Route {
	use Mechanism\Settings;
	/**
	 * Class for dynamic route rules
	 * 
	 * TODO: Normalaize names 
	 * 	     Exceptions
	 * 		 trim
	 * 		 url_decode
	 * 		 Test errors
	 * 		 Normalaize regex's
	 * 			
	 * @uses Mechanism\Request\Http
	 * @uses Mechanism\Settings
	 * @author BreatheInMyVoid ( BreatheInMyVoid@gmail.com )
	 */
	class Dynamic extends Route {	
		/**
		 * Path matrix
		 * 
		 * $path[0] = array ( 'name' => 'regex' ),
		 * $path[1] = array ( 'name' => 'regex' )
		 *  etc..
		 * 		  
		 * @var array
		 */
		protected $path = array( );
		
		/**
		 * Order
		 * 
		 * Storing order of path pieces here
		 * 
		 * @see set_dynamic_part( )
		 * @see set_static_part( )
		 * @var integer
		 */
		protected $part_order = 0;
		
		/**
		 * Build regex pattern for matching
		 * 
		 * 
		 * @var array $path
		 */
		protected function assemble( $path_matrix ) {		
			/* Build regex */
			$regex = '/^\/';
			for ( $i = 0; $i < count( $path_matrix ); $i++ ) {
				$regex .=  current( $path_matrix[ $i ] ) . '\/';
			}	
			$regex .= ( $this->is_vars_enabled ) ? '[' . $this->vars_regex  . '\/]*$/' : '$/';
			
			return  $regex;			
		}
		
		/**
		 * Build regex pattern for default matching route
		 * 
		 * Build regex pattern for matching route
		 * with default controller, action ... etc.
		 * 
		 * TODO: Reconsider regex policy
		 * 		 Add modules
		 * 
		 * @var array $this->part 
		 */
		protected function assemble_default( $path_matrix ) {
			/* Build regex */
			$regex = '/^\/';
			for ( $i = 0; $i < count( $path_matrix ); $i++ ) {
				if ( ':module'     === key( $path_matrix[ $i ] ) ||
					 ':controller' === key( $path_matrix[ $i ] ) ||
				     ':action' 	=== key( $path_matrix[ $i ] ) ) {
					continue;	
				} else {
					$regex .=  current( $path_matrix[ $i ] ) . '\/';
				}
			}	
			$regex .= '*/';
			
			return  $regex;					
		}
		
		/**
		 * Disassemble requested route
		 * 
		 * This function disassembles requested route
		 * 
		 * TODO: Reconsider normalization policy
		 * 		 Reconsider speed and safty 
		 * 
		 * @param string
		 */
		protected function disassemble( $requested_url ) {
			/* Nornalize */
			if ( substr( $requested_url, 0, 1 ) === '/' ) {
				$requested_url = substr( $requested_url, 1 );
			}
			
			if ( substr( $requested_url, -1 ) === '/' ) {
				$requested_url = substr( $requested_url, 0, -1 );
			}			
			
			/* Disassemble url */
			$disassembled_url = explode( '/', $requested_url );
			$disassembled_url_len = count( $disassembled_url );
			$path_len = count( $this->path );
				
			for ( $i = 0; $i < $path_len; $i++ ) {
				$key = key( $this->path[ $i ] );
				
				if ( ':module' === $key ) {
					$this->set_module( 
						( isset( $disassembled_url[ $i ] ) && !empty( $disassembled_url[ $i ] ) ) ?
							$disassembled_url[ $i ] : Settings::module 
					 );	
				} else if ( ':controller' === $key ) {
					$this->set_controller( 
						( isset( $disassembled_url[ $i ] ) && !empty( $disassembled_url[ $i ] ) ) ?
							$disassembled_url[ $i ] : Settings::controller 
					 );	
				} else if ( ':action' === $key ) {
					$this->set_action( 
						( isset( $disassembled_url[ $i ] ) && !empty( $disassembled_url[ $i ] ) ) ?
							$disassembled_url[ $i ] : Settings::action 
					 );	
				} else if ( ':static' === $key ) {
					continue;	
				} else {
					/* Store dynamic variables */
					//$this->request->set_get( key( $this->path[ $i ] ), $disassembled_url[ $i ] );
					$this->vars[ key( $this->path[ $i ] ) ] = $disassembled_url[ $i ];
				}
			}
			
			/* If vars enables get vars from end of path */
			if ( $this->is_vars_enabled && $disassembled_url_len > $path_len ) {
				$vars_length = $disassembled_url_len - $path_len;
					
				/* Store variables in form to $_GET */
				for ( $j = $path_len + 1; $j < $disassembled_url_len; $j+=2 ) {
					//$this->request->set_get( $disassembled_url[ $j - 1 ], $disassembled_url[ $j ] );
					$this->vars[ $disassembled_url[ $j - 1 ] ] =  $disassembled_url[ $j ];
				}
			}		
		}
			
		/**
		 * Set static part of route 
		 * 
		 * Set static part of route and store rule to
		 * path variable
		 * 
		 * @param string
		 * @return void
		 */
		protected function set_static_part( $path_name ) {
			$i = $this->part_order++;

			if ( !isset( $this->path[ $i ] ) ) {
				$this->path[ $i ] = array ( ':static' => $path_name );
			} else {
				//Exception
			}	
		}
		
		/**
		 * Set dynamic part of route 
		 * 
		 * Set dynamic part of route and store rule to
		 * path variable.
		 * Reserved: 
		 * 		:module
		 * 		:controller
		 * 		:action
		 * 		:static
		 * 
		 * @param string
		 * @param string 
		 * @return void
		 */
		protected function set_dynamic_part( $path_name, $regex = 'a-z0-9' ) {
			$i = $this->part_order++;
			
			switch ( $path_name ) {
				case ':module':
					if ( !isset( $this->path[ $i ] ) ) {
						$this->path[ $i ] = array ( ':module' => '[' . $regex . ']+' );
					} else {
						//Exception
					}	
					break;
				case ':controller':
					if ( !isset( $this->path[ $i ] ) ) {
						$this->path[ $i ] = array ( ':controller' => '[' . $regex . ']+' );
					} else {
						//Exception
					}	
					break;
				case ':action':
					if ( !isset( $this->path[ $i ] ) ) {
						$this->path[ $i ] = array ( ':action' => '[' . $regex . ']+' );
					} else {
						//Exception
					}					
					break;
				default:
					if ( !isset( $this->path[ $i ] ) ) {
						$this->path[ $i ] = array ( $path_name => '[' . $regex . ']+' );
					} else {
						//Exception
					}								
			}	
		}

		/**
		 * Constructor
		 * 
		 * Route rule:
		 *     ~ 			- dynamic part
		 *     :module     - module
		 *     :controller - controller
		 *     :action     - action
		 * 	   *            - on end means variables 
		 * 	   static parts simple self named 
		 * 
		 * @var string
		 */
		public function __construct( $route ) {
			$route = explode( '/', $route );
			$route_len = count( $route ); 

			/* TODO: We can do this better */
			for ( $i = 0; $i < $route_len; $i++ ) {
				if ( '~' === substr( $route[ $i ], 0, 1 ) ) {
					$this->set_dynamic_part( substr( $route[ $i ], 1 ) );
				} else if ( ':module' === $route[ $i ] ) {
					$this->set_dynamic_part( $route[ $i ] );
				} else if ( ':controller' === $route[ $i ] ) {
					$this->set_dynamic_part( $route[ $i ] );
				} else if ( ':action' === $route[ $i ] ) {
					$this->set_dynamic_part( $route[ $i ] );
				} else if ( '*' === $route[ $i ] ) {
					$this->is_vars_enabled = true;
				} else if ( $route[ $i ] ) {
					$this->set_static_part( $route[ $i ] );
				} else {
					continue;
				}
			}
		}
		
		/**
		 * Set regex rule for route part
		 * 
		 * @param string
		 * @param string regex
		 * @return fluent interface
		 */
		public function set_rule( $part, $rule ) {
			$path_len = count( $this->path );

			if ( '*' === $part ) {
				$this->vars_regex = $rule;		
				return $this;
			} else {			
				for ( $i = 0; $i < $path_len; $i++ ) {
					if ( $part === key( $this->path[ $i ] ) ) {
						$this->path[ $i ][$part] = $rule;
					}
				}
			}
			
			return $this;
		}
		
		/**
		 * Matching function
		 * 
		 * @param string
		 * @return bool
		 */
		public function match( $uri ) {
			$pattern = $this->assemble( $this->path );
			$default_pattern = $this->assemble_default( $this->path );

			/*	
			echo '<br />';
			var_dump( $pattern );
			echo '<br />';
			var_dump( $default_pattern );
			echo '<br />';
			*/

			if ( preg_match( $pattern, $uri ) ) {
				$this->disassemble( $uri );
				return true;
			} else if ( preg_match( $default_pattern, $uri ) ) {
				$this->disassemble( $uri );
				return true;
			} else {
				return false;
			}
		}	
	}
}
